﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes;

namespace FCSChart.Graphical
{
    /// <summary>
    /// X轴段选门
    /// </summary>
    public class SegmentXGraphical : BaseGraphical
    {
        private Point? x12;

        public Point? X12
        {
            get { return x12; }
            private set { x12 = value; OnPropertyChanged("X12"); }
        }

        public SegmentXGraphical() { }
        public SegmentXGraphical(SegmentXGraphicalModel model) : base(model)
        {
            this.X12 = new Point(model.X1, model.X2);
            if (model.AreaNames != null && model.AreaNames.Length == 1)
            {
                Areas = model.AreaNames.Select(p => new GraphicalArea() { Name = p, OwnerGraphical = this }).ToArray();
                Helper.AddExistedGraphicalName(this.ShortName, model.AreaNames.ToArray());
                if (model.AreaColors != null && model.AreaColors.Length == 1)
                {
                    Areas[0].DisplayColor = model.AreaColors[0];
                }
            }
        }
        protected override void InitName()
        {
            this.Name = "SegmentX";
            this.ShortName = "SX";
        }
        #region 容器事件--绘制图形时使用
        internal override void PanelMouseDown(object sender, MouseButtonEventArgs e)
        {
            if (IsCreateing && sender is Panel panel)
            {
                var point = e.GetPosition(panel);
                var x = OwnerChart.XAxis.GetLocationValue(point.X);
                X12 = new Point(x, x);
            }
        }

        internal override void PanelMouseMove(object sender, MouseEventArgs e)
        {
            if (IsCreateing && sender is Panel panel)
            {
                var point = e.GetPosition(panel);
                var x = OwnerChart.XAxis.GetLocationValue(point.X);
                if (X12 != null) X12 = new Point(x12.Value.X, x);
            }
            else if (!IsCreateing && ControledShape != null)
            {
                Shape_MouseMove(ControledShape, e);
            }
        }

        internal override void PanelMouseUp(object sender, MouseButtonEventArgs e)
        {
            if (IsCreateing)
            {
                if (X12.Value.X == X12.Value.Y) return;
                IsCreateing = false;
            }
            else if (ControledShape != null)
            {
                Shape_MouseLeftButtonUp(ControledShape, e);
                ControledShape = null;
            }
        }
        #endregion

        #region 图形移动变形

        internal override void Drawing()
        {
            if (OwnerChart == null || !OwnerChart.IsLoaded || OwnerChart.XAxis == null || !OwnerChart.XAxis.IsLoaded || OwnerChart.XAxis.ActualWidth == 0) return;
            if (GraphicalShape == null)
            {
                var temp = new Path() { Cursor = Cursors.Hand };
                temp.SetBinding(Path.DataProperty, new Binding("X12") { Source = this, Converter = Converters.SegmentXPointsToGeometryConverter.Converter, ConverterParameter = this.OwnerChart, Mode = BindingMode.OneWay });

                temp.SetBinding(Shape.FillProperty, new Binding("Fill") { Source = this });
                temp.SetBinding(Shape.StrokeProperty, new Binding("Stroke") { Source = this });
                temp.SetBinding(Shape.StrokeThicknessProperty, new Binding("StrokeThickness") { Source = this });
                temp.SetBinding(FrameworkElement.ContextMenuProperty, new Binding("ContextMenu") { Source = this });
                temp.SetBinding(UIElement.FocusableProperty, new Binding("CanChangeGraphical") { Source = this.OwnerChart });
                temp.MouseDown += Graphical_MouseDown;
                temp.MouseMove += Graphical_MouseMove;
                temp.MouseLeftButtonUp += (sender, e) =>
                {
                    if (this.OwnerChart.CanChangeGraphical && !IsCreateing) RefreshAreaSource(this.OwnerChart.XValueConverter, this.OwnerChart.YValueConverter);
                };
                temp.KeyDown += (sender, e) =>
                {
                    if (this.OwnerChart.CanChangeGraphical)
                    {
                        if (e.Key == Key.Left)
                        {
                            Move(-1, 0); e.Handled = true;
                        }
                        else if (e.Key == Key.Right)
                        {
                            Move(1, 0); e.Handled = true;
                        }
                    }
                };
                GraphicalShape = temp;
            }
            OnPropertyChanged("X12");
            if (!IsCreateing) DrawingControl();
        }
        internal override void Move(double x, double y)
        {
            if (x != 0)
            {
                var tempx = OwnerChart.XAxis.GetLocationValue(OwnerChart.XAxis.GetValueLocation(X12.Value.X) + x);
                var tempy = OwnerChart.XAxis.GetLocationValue(OwnerChart.XAxis.GetValueLocation(X12.Value.Y) + x);
                X12 = new Point(tempx, tempy);
                DrawingControl();
            }
        }

        #endregion


        #region 门控制相关
        protected override void DrawingControl()
        {
            if (GraphicalShape is Path graphical && graphical.Data is PathGeometry geometry && geometry.Figures.Count == 1 && geometry.Figures[0].Segments.Count == 4)
            {
                if (Areas == null) Areas = new GraphicalArea[] { new GraphicalArea() { Name = OwnerChart.CreateNewAreaNameFunction(this), OwnerGraphical = this } };
                this.OwnerChart.AddGraphicalArea(Areas);
                List<Point> points = new List<Point>();
                for (int i = 0; i < 2; i++)
                {
                    var segment = geometry.Figures[0].Segments[i] as LineSegment;
                    var point = new Point(segment.Point.X, segment.Point.Y / 2);
                    points.Add(point);
                    if (ControlShapes.Count > i)
                    {
                        if (ControlShapes[i] is Path path && path.Data is EllipseGeometry ellipse)
                            ellipse.Center = point;
                    }
                    else
                    {
                        ControlShapes.Add(new Path() { Data = new EllipseGeometry(point, 5, 5), Cursor = Cursors.SizeWE });
                    }
                }
                Areas[0].Center = new Point(points.Average(p => p.X), points.Average(p => p.Y));
            }
        }

        protected override void Shape_MouseMove(object sender, MouseEventArgs e)
        {
            if (this.OwnerChart.CanChangeGraphical && e.LeftButton == MouseButtonState.Pressed && sender is Path path && path.Data is EllipseGeometry ellipse)
            {
                var point = e.GetPosition(OwnerChart.ViewPanel);
                var index = ControlShapes.IndexOf(path);
                var value = OwnerChart.XAxis.GetLocationValue(point.X);
                if (index == 0) X12 = new Point(value, x12.Value.Y);
                else X12 = new Point(x12.Value.X, value);
                e.Handled = true;
                ellipse.Center = point;
            }
        }
        protected override void Shape_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            if (this.OwnerChart.CanChangeGraphical) DrawingControl();
            base.Shape_MouseLeftButtonUp(sender, e);
        }
        #endregion
        /// <summary>
        /// 更新门划分的区域内数据
        /// </summary>
        internal override async void RefreshAreaSource(Func<object, double> xValueConverter, Func<object, double> yValueConverter)
        {
            if (Areas == null || Areas.Length != 1) return;
            if (!isCreateing && X12.HasValue && OwnerChart != null && OwnerChart.XSource != null)
            {
                var min = Math.Min(X12.Value.X, X12.Value.Y);
                var max = Math.Max(X12.Value.X, X12.Value.Y);
                var items = OwnerChart.XSource;
                var count = items.Count;
                var parentIndexs = OwnerChart.Indexs;

                var maxDegreeOfParallelism = OwnerChart.Series == null ? 4 : OwnerChart.Series.MaxDegreeOfParallelism;
                try
                {
                    IsRefreshingAreaSource = true;
                    if (CancelTokenSource != null)
                    {
                        CancelTokenSource.Cancel();
                        CancelTokenSource.Dispose();
                    }
                    CancelTokenSource = new CancellationTokenSource();
                    var indexstemp = await Task.Factory.StartNew((tkn) =>
                    {
                        if (tkn is CancellationToken token)
                        {
                            ConcurrentBag<int> indexs = new ConcurrentBag<int>();
                            if (parentIndexs == null)
                            {
                                try
                                {
                                    var result = Parallel.For(0, count, new ParallelOptions() { CancellationToken = token, MaxDegreeOfParallelism = maxDegreeOfParallelism }, (i, loop) =>
                                    {
                                        if (loop.IsStopped) return;
                                        if (items.Count <= i || token.IsCancellationRequested) loop.Stop();
                                        else
                                        {
                                            var v = xValueConverter == null ? Convert.ToDouble(items[i]) : xValueConverter(items[i]);
                                            if (v >= min && v <= max) indexs.Add(i);
                                        }
                                    });
                                    if (!result.IsCompleted) return null;
                                }
                                catch (System.OperationCanceledException) { return null; }
                            }
                            else
                            {
                                try
                                {
                                    var result = Parallel.ForEach(parentIndexs, new ParallelOptions() { CancellationToken = token, MaxDegreeOfParallelism = maxDegreeOfParallelism }, (i, loop) =>
                                    {
                                        if (loop.IsStopped) return;
                                        if (items.Count <= i || token.IsCancellationRequested) loop.Stop();
                                        else
                                        {
                                            var v = xValueConverter == null ? Convert.ToDouble(items[i]) : xValueConverter(items[i]);
                                            if (v >= min && v <= max) indexs.Add(i);
                                        }
                                    });
                                    if (!result.IsCompleted) return null;
                                }
                                catch (System.OperationCanceledException) { return null; }
                            }
                            return indexs.ToArray();
                        }
                        else return null;
                    }, CancelTokenSource.Token, CancelTokenSource.Token);

                    if (Areas == null || Areas.Length != 1 || indexstemp == null) return;
                    if (CancelTokenSource != null)
                    {
                        CancelTokenSource.Dispose();
                        CancelTokenSource = null;
                    }
                    Areas[0].InsideIndexs = indexstemp;
                    IsRefreshingAreaSource = false;
                }
                catch (TaskCanceledException) { }
            }
            else
            {
                Areas[0].InsideIndexs = null;
            }
        }
        /// <summary>
        /// 获取门的数据类型
        /// </summary>
        /// <returns></returns>
        public override BaseGraphicalModel GetGraphicalMode()
        {
            if (IsCreateing) return null;
            return new SegmentXGraphicalModel() { AreaNames = new string[] { Areas[0].Name }, AreaColors = new Color[] { Areas[0].DisplayColor }, X1 = X12.Value.X, X2 = X12.Value.Y };
        }
    }

    public class SegmentXGraphicalModel : BaseGraphicalModel
    {
        public double X1 { get; set; }
        public double X2 { get; set; }

    }
}
